package pl.edu.agh.mguesser.audioengine;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.util.concurrent.Executor;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import pl.edu.agh.mguesser.concurrent.ExecutorFactory;

/**
 * @author keiran
 */
@Service
public class AudioEngineImpl implements AudioEngine {

    private final static Logger LOGGER 
            = LoggerFactory.getLogger(AudioEngineImpl.class);
    
    static final int THREADS = 2;
    
    private Executor executor;
    private PlayerFactory playerFactory = new SimplePlayerFactory();

    public AudioEngineImpl() {
        this(new ExecutorFactory());
    }

    AudioEngineImpl(ExecutorFactory executorFactory) {
        ThreadFactoryBuilder builder = new ThreadFactoryBuilder().setDaemon(true);
        this.executor = executorFactory.createFixedThreadPool(THREADS, builder);
    }
    
    @Override
    public StreamInProgress play(Path path) throws AudioEngineException {
        try {
            Player player = playerFactory.create(path);
            executor.execute(new PlayTask(player));
            return new JLayerStreamInProgress(player);

        } catch (JavaLayerException | FileNotFoundException ex) {
            throw new AudioEngineException(ex);

        }
    }
    
    static class PlayTask implements Runnable {
        
        private Player player;

        public PlayTask(Player player) {
            this.player = player;
        }

        @Override
        public void run() {
            try {
                player.play();
            } catch (JavaLayerException ex) {
                LOGGER.error(ex.getMessage(), ex);
            }
        }

        public Player getPlayer() {
            return player;
        }
        
    }

    Executor getExecutor() {
        return executor;
    }

    void setPlayerFactory(PlayerFactory playerFactory) {
        this.playerFactory = playerFactory;
    }
    
}

interface PlayerFactory {
    Player create(Path path) throws JavaLayerException, FileNotFoundException;
}

class SimplePlayerFactory implements PlayerFactory {

    @Override
    public Player create(Path path) throws JavaLayerException, FileNotFoundException {
        return new Player(new FileInputStream(path.toFile()));
    }
    
}
